package com.sailing.codegenerate.database.code;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import com.sailing.codegenerate.database.db.TabInfos;
import com.sailing.codegenerate.database.db.TableReader;
import com.sailing.codegenerate.database.template.TemplateConfig;
import com.sailing.codegenerate.database.template.TemplateConfigReader;

import freemarker.template.Configuration;
import freemarker.template.Template;

public class CodeFileWriter {

	TableReader entityReader = new TableReader();

	TabInfos tabInfos = null;

	TemplateConfig templateConfig = null;

	CodeFileConfig codeFileConfig = null;

	public CodeFileWriter(CodeFileConfig codeFileConfig) {
		this.codeFileConfig = codeFileConfig;
	}

	public void init() throws Exception {
		templateConfig = TemplateConfigReader.getInstance().readProperties();
		tabInfos = entityReader.getTabInfos(codeFileConfig.getTableName(), codeFileConfig.getPkIdFieldName());
	}

	public Configuration getConfiguration() throws IOException {
		@SuppressWarnings("deprecation")
		Configuration cfg = new Configuration();
		String path = this.templateConfig.getTemplatePath();
		File templateDirFile = new File(path);
		cfg.setDirectoryForTemplateLoading(templateDirFile);
		cfg.setLocale(Locale.CHINA);
		cfg.setDefaultEncoding(templateConfig.getEncoding());
		return cfg;
	}

	/**
	 * 生成实体文件
	 *
	 * @throws IOException
	 */
	public void generateEntityFiles() throws IOException {
		try {
			this.generateFile("java_entity.ftl", this.getEntityFileName());
			System.out.println("成功生成java_entity文件,文件地址：" + this.getEntityFileName());
		} catch (Exception e) {
			throw new IOException("生成java_entity文件出现错误....", e);
		}
	}

	/**
	 * 生成dao文件
	 *
	 * @throws IOException
	 */
	public void generateDaoFiles() throws IOException {
		try {
			this.generateFile("java_dao.ftl", this.getDaoFileName());
			System.out.println("成功生成java_dao文件,文件地址：" + this.getDaoFileName());
		} catch (Exception e) {
			throw new IOException("生成java_dao文件出现错误....", e);
		}
	}

	/**
	 * 生成service文件
	 *
	 * @throws IOException
	 */
	public void generateServiceFiles() throws IOException {
		try {
			this.generateFile("java_service.ftl", this.getServiceFileName());
			System.out.println("成功生成java_service文件,文件地址：" + this.getServiceFileName());
		} catch (Exception e) {
			throw new IOException("生成java_service文件出现错误....", e);
		}
	}

	/**
	 * 生成controller文件
	 *
	 * @throws IOException
	 */
	public void generateControllerFiles() throws IOException {
		try {
			this.generateFile("java_controller.ftl", this.getControllerFileName());
			System.out.println("成功生成java_controller文件,文件地址：" + this.getControllerFileName());
		} catch (Exception e) {
			throw new IOException("生成java_controller文件出现错误....", e);
		}
	}

	/**
	 * 生成java_entityVO文件
	 *
	 * @throws IOException
	 */
	public void generateEntityVoFiles() throws IOException {
		try {
			this.generateFile("java_entityVO.ftl", this.getEntityVoFileName());
			System.out.println("成功生成java_entityVO文件,文件地址：" + this.getEntityVoFileName());
		} catch (Exception e) {
			throw new IOException("生成java_entityVO文件出现错误....", e);
		}
	}

	/**
	 * 生成java_entityVO文件
	 *
	 * @throws IOException
	 */
	public void generateEntityBOFiles() throws IOException {
		try {
			this.generateFile("java_entityBO.ftl", this.getEntityBOFileName());
			System.out.println("成功生成java_entityBO文件,文件地址：" + this.getEntityBOFileName());
		} catch (Exception e) {
			throw new IOException("生成java_entityBO文件出现错误....", e);
		}
	}

	protected void generateFile(String templateFile, String generateFilePath) throws Exception {
		String fileDir = generateFilePath.substring(0, generateFilePath.lastIndexOf("/"));
		forceMkdir(new File(fileDir + "/"));
		Writer out = new OutputStreamWriter(new FileOutputStream(generateFilePath), templateConfig.getEncoding());
		Template template = getConfiguration().getTemplate(templateFile);
		template.process(this.getTemplateData(), out);
		out.close();
	}

	/**
	 * Makes a directory, including any necessary but nonexistent parent
	 * directories. If a file already exists with specified name but it is not a
	 * directory then an IOException is thrown. If the directory cannot be created
	 * (or does not already exist) then an IOException is thrown.
	 *
	 * @param directory
	 *            directory to create, must not be <code>null</code>
	 * @throws NullPointerException
	 *             if the directory is <code>null</code>
	 * @throws IOException
	 *             if the directory cannot be created or the file already exists but
	 *             is not a directory
	 */
	public static void forceMkdir(File directory) throws IOException {
		if (directory.exists()) {
			if (!directory.isDirectory()) {
				String message = "File " + directory + " exists and is "
						+ "not a directory. Unable to create directory.";
				throw new IOException(message);
			}
		} else {
			if (!directory.mkdirs()) {
				// Double-check that some other thread or process hasn't made
				// the directory in the background
				if (!directory.isDirectory()) {
					String message = "Unable to create directory " + directory;
					throw new IOException(message);
				}
			}
		}
	}

	protected Map<String, Object> getTemplateData() {
		Map<String, Object> templateData = new HashMap<>(8);
		templateData.put("tabInfos", tabInfos);
		templateData.put("templateConfig", templateConfig);
		templateData.put("codeFileConfig", codeFileConfig);
		return templateData;
	}

	protected String getEntityFileName() {
		return templateConfig.getProjectDir() + "/" + codeFileConfig.getTableName() + "/" + tabInfos.getEntityName()
				+ "PO.java";
	}

	protected String getDaoFileName() {
		return templateConfig.getProjectDir() + "/" + codeFileConfig.getTableName() + "/" + tabInfos.getEntityName()
				+ "Repository.java";
	}

	protected String getServiceFileName() {
		return templateConfig.getProjectDir() + "/" + codeFileConfig.getTableName() + "/" + tabInfos.getEntityName()
				+ "Service.java";
	}

	protected String getControllerFileName() {
		return templateConfig.getProjectDir() + "/" + codeFileConfig.getTableName() + "/" + tabInfos.getEntityName()
				+ "Controller.java";
	}

	protected String getEntityVoFileName() {
		return templateConfig.getProjectDir() + "/" + codeFileConfig.getTableName() + "/" + tabInfos.getEntityName()
				+ "VO.java";
	}

	protected String getEntityBOFileName() {
		return templateConfig.getProjectDir() + "/" + codeFileConfig.getTableName() + "/" + tabInfos.getEntityName()
				+ "BO.java";
	}
}
